#ifndef _Event_H_
#define _Event_H_

//
// Event.h
//
// Created by TB on 11/17/11.
// Copyright 2011 BNL. All rights reserved.
//

#include <vector>

#ifndef __CINT__
#include <boost/shared_ptr.hpp>
#endif

#include <TClonesArray.h>
#include <TObject.h>
#include <TVector3.h>
#include <TParticle.h>

#include "tppmc/Particle.h"
#include "tppmc/PartonDistribution.h"
namespace tppmc {
   
  class Distributions;
  class FragmentationFunction;
   
  /**
     An event, containing partons involved in the hard scattering, and hadrons
     produced from their fragmentation.
  */
  class Event : public TObject {
      
  public:
      
    /**
       Destructor.
    */
    virtual ~Event() { }
      
    /**
       Returns the PYTHIA process ID the generated the event.
    */
    virtual int Process() const { return mProcessId; }
      
    /**
       Returns the momentum fraction carried by the first parton
       in the hard interaction.
    */
    virtual double PartonX1() const { return mScatter.at(0).x; }
      
    /**
       Returns the momentum fraction carried by the second parton
       in the hard interaction.
    */
    virtual double PartonX2() const { return mScatter.at(1).x; }
      
    /**
       Returns the scale of the scattering process.
    */
    virtual double QSquared() const { return mQSquared; }
      
    /**
       Returns the transvesre momentum of the hard subprocess
       in the rest frame of the hard interaction.
    */
    virtual double PartonPt() const { return 0.; }

    /* Return the Sivers weight of the first parton in the hard scattering */
    virtual double SiversWeight1() const;
    /* Return the Sivers weight of the second parton in the hard scattering */
    virtual double SiversWeight2() const;
    /* Return the spin factor of the first parton in the hard scattering */
    virtual double SiversSpinFactor1() const;
    /* Return the spin factor of the second parton in the hard scattering */
    virtual double SiversSpinFactor2() const;
      
    /**
       Returns the (x, y, z) coordinates of the collision
       point in the laboratory frame.
    */
    virtual TVector3 Vertex() const { return TVector3(); }
      
    /**
       Returns the spin (-1, 0 or +1) of the first beam.
    */
    virtual short Spin1() const { return 0; }
      
    /**
       Returns the spin (-1, 0 or +1) of the second beam.
    */
    virtual short Spin2() const { return 0; }
      
    /**
       Returns the phi rotation applied to the event.
    */
    virtual double Rotation() const { return 0.; }
      
    /** Set the event plane rotation to apply to all particles */
    virtual void SetRotation(double radians) { mRotation = radians; }
      
    /**
       Returns the list of partons in the event.
    */
    virtual std::vector<tppmc::Parton*> Partons() const;
      
    /**
       Returns the list of hadrons in the event.
    */
    virtual std::vector<tppmc::Hadron*> Hadrons() const;
      
    virtual std::vector<TParticle*> Particles() const;

    /**
       Fragment all partons in the event record into hadrons using
       the specified fragmentation function.
       Returns the number of new hadrons generated.
    */
    virtual int Fragment(FragmentationFunction&);
      
    /**
       Add a parton to the parton list.
       Returns the new size of the parton list.
    */
    virtual unsigned AddParton(const Parton&);
      
    /**
       Add a hadron to the hadron list.
       Returns the new size of the hadron list.
    */
    virtual unsigned AddHadron(const Hadron&);


    virtual unsigned AddParticle(const TParticle&);
      
    /**
       Private constructor to force use of EventFactory.
       Constructs an event using the current state of TPythia6.
       Optionally provide a phi angle (between 0 and 2pi in radians) by
       which to rotate the event coordinates from the generated plane.
       \remark public for debug
    */
    Event(double rotation = 0., double etamin = -1000.,
	  double etamax = 1000., double minpt = 0., long tparticleout = 0);
      
    /** Returns the fragmentation multiplicity of the nth parton */
    unsigned Multiplicity(unsigned n) const;
      
    /**
       Set the event contents from the current state of TPythia6.
    */
    unsigned Update(Distributions&);
      
    /**
       For debug
    */
    Hadron HadronSum(int group) const;
      
    //   protected:
      
    /**
       Populate the parton and hadron lists from the
       current state of PYTHIA.
    */
    void FillParticles(Distributions&);
      
    /** Fragment a single parton. Return the number of hadrons is produces */
    virtual int FragmentParton(Parton&, FragmentationFunction&);
      
#ifndef __CINT__
    typedef boost::shared_ptr<PartonDistribution> Pdf;
    double ComputeSiversWeight(const Parton&, Distributions&);
    double ComputeSiversSpinFactor(const Parton&);
    double ComputeTransverseSpin(const Parton&, Distributions&);
#endif
    void ComputeCentreOfMassEnergy();
      
    Int_t mProcessId;
    Double32_t mQSquared;
    Double32_t mPartonPt;
    Double32_t mCentreOfMassEnergy;
    std::vector<Parton> mScatter; ///< Partons from each beam at the hard
    ///< scattering. Always has 2 elements,
    ///< the first from the +z beam, the second
    ///< from the -z beam.
    Double32_t mRotation;
    double mEtaMin; //!
    double mEtaMax; //!
    double mMinPt; //!
    long mtparticleout;      
      
    TClonesArray mPartons; ///< Parton after scattering/radiation, which
    ///< are passed to fragmentation
    TClonesArray mHadrons; ///< Final-state particles from fragmentation
      

    TClonesArray mParticle; ///<TParticle array


    friend class EventFactory;
      
    ClassDef(tppmc::Event, 1)
  };
   
  inline double Event::SiversWeight1() const {
    return mScatter.at(0).SiversWeight();
  }
  inline double Event::SiversWeight2() const {
    return mScatter.at(1).SiversWeight();
  }
  inline double Event::SiversSpinFactor1() const {
    return mScatter.at(0).SiversSpinFactor();
  }
  inline double Event::SiversSpinFactor2() const {
    return mScatter.at(1).SiversSpinFactor();
  }
   
  /**
     \todo Move event-building code to EventFactory.
     Build Event objects.
  */
  class EventFactory : public TObject {
      
  public:
      
    /**
       Use the specified fragmentation function to fragment
       partons into hadrons when building events.
    */
    EventFactory(FragmentationFunction* = NULL) { }
      
    virtual ~EventFactory() { }
      
    Event* Create(double rotation = 0.);
      
    ClassDef(tppmc::EventFactory, 1)
      };
   
} // namespace tppmc

#endif
